// Fill out your copyright notice in the Description page of Project Settings.


#include "HotChunkComponent.h"
#include "Interfaces/IHttpResponse.h"
#include "Kismet/GameplayStatics.h"
#include "HotChunkLibrary.h"
#include "IPlatformFilePak.h"
#include "HttpModule.h"
#include "PlatformFilemanager.h"
#include "FileManager.h"
#include "FileHelper.h"
#include "Json.h"



// Sets default values for this component's properties
UHotChunkComponent::UHotChunkComponent()
{
	// Set this component to be initialized when the game starts, and to be ticked every frame.  You can turn these features
	// off to improve performance if you don't need them.
	PrimaryComponentTick.bCanEverTick = true;

	InstallChunkContentIndex = -1;
}


// Called when the game starts
void UHotChunkComponent::BeginPlay()
{
	Super::BeginPlay();

}



// Called every frame
void UHotChunkComponent::TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction)
{
	Super::TickComponent(DeltaTime, TickType, ThisTickFunction);

	if (InstallChunkContentIndex >= 0)
	{
		float DownloadPercent = 1.f / ChunkPendingContents.Num();

		//HotChunkProgressSumbit.ExecuteIfBound(CurrentDownloadSize / RequireDownloadSize);
		HotChunkProgressSumbit.ExecuteIfBound((ChunkPendingContents[InstallChunkContentIndex]->GetInstallProgress() + InstallChunkContentIndex) * DownloadPercent);

		HotChunkStateSumbit.ExecuteIfBound(FString::Printf(TEXT("Updating : [%d/%d] Speed : %f MB/s"), InstallChunkContentIndex + 1, ChunkPendingContents.Num(), ChunkPendingContents[InstallChunkContentIndex]->GetDownloadSpeed()));
	}
}

void UHotChunkComponent::MainProcess()
{
	// init data
	TempStorageFolder = FPaths::Combine(FPaths::ProjectDir(), TempStoragePath);
	ChunkStorageFloder = FPaths::Combine(FPaths::ProjectDir(), ChunkStoragePath);

	if (!IFileManager::Get().DirectoryExists(*TempStorageFolder))
		IFileManager::Get().MakeDirectory(*TempStorageFolder);

	if (!IFileManager::Get().DirectoryExists(*ChunkStorageFloder))
		IFileManager::Get().MakeDirectory(*ChunkStorageFloder);

	// no ChunkContent Installing
	InstallChunkContentIndex = -1;

	if (UGameplayStatics::DoesSaveGameExist(HotChunkSaveName, 0))
	{
		HotChunkSave = Cast<UHotChunkSave>(UGameplayStatics::LoadGameFromSlot(HotChunkSaveName, 0));

		CurrentVersionManifestInfo = UHotChunkFile::ConvertJsonToVersionManifestInfo(HotChunkSave->VersionManifest);

		CheckAndClearHotChunk();

		//UE_LOG(LogTemp, Log, TEXT("HotChunk CurrentVersionManifestInfo[%s]"), *HotChunkSave->VersionManifest);

	}
	else
	{
		HotChunkSave = Cast<UHotChunkSave>(UGameplayStatics::CreateSaveGameObject(UHotChunkSave::StaticClass()));
		HotChunkSave->WebSites = VersionWebSites;
	}

	RequireWebSiteIndex = 0;

	SaveHotChunkStatus(EHotChunkStatus::LoadWebInfo);

	FHttpRequestPtr HttpRequest = FHttpModule::Get().CreateRequest();
	HttpRequest->SetURL(FPaths::Combine(HotChunkSave->WebSites[RequireWebSiteIndex], VersionWebName));
	HttpRequest->SetVerb(TEXT("GET"));
	HttpRequest->SetHeader(TEXT("Content-Type"), TEXT("application/json"));
	HttpRequest->OnProcessRequestComplete().BindUObject(this, &UHotChunkComponent::RequireVersionWebCompleted);
	HttpRequest->ProcessRequest();

}

void UHotChunkComponent::CheckAndClearHotChunk()
{
	switch (HotChunkSave->HotChunkStatus)
	{
	case EHotChunkStatus::LoadWebInfo:
	{
		HotChunkSave->HotChunkStatus = EHotChunkStatus::HotChunkCompleted;
	}
	break;
	case EHotChunkStatus::LoadVersionManifest:
	{
		HotChunkSave->HotChunkStatus = EHotChunkStatus::HotChunkCompleted;
	}
	break;
	case EHotChunkStatus::LoadChunkContents:
	{
		//Temp Floder have file, Chunk Floder no File, Current Version Manifest is empty
	}
	break;
	case EHotChunkStatus::InstallChunkContents:
	{
		//Temp Floder have file, Chunk Floder have some File, Current Version Manifest is not complete
	}
	break;
	}

	HotChunkSave->HotChunkStatus = EHotChunkStatus::HotChunkCompleted;

	if (!CurrentVersionManifestInfo.IsValid())
		return;


	// Clear Temp Folder Files
	TArray<FString> AbandonFileNames;
	IFileManager::Get().FindFiles(AbandonFileNames, *(TempStorageFolder / TEXT("*.*")), true, false);
	for (auto AbandonFileName : AbandonFileNames)
		IFileManager::Get().Delete(*FPaths::Combine(TempStorageFolder, AbandonFileName));

	// Clear Chunk Floder Manifest Files
	AbandonFileNames.Reset();
	IFileManager::Get().FindFiles(AbandonFileNames, *(ChunkStorageFloder / TEXT("*.manifest")), true, false);
	for (auto AbandonFileName : AbandonFileNames)
		IFileManager::Get().Delete(*FPaths::Combine(ChunkStorageFloder, AbandonFileName));

	TArray<FString> AllChunkFileNames;
	IFileManager::Get().FindFiles(AllChunkFileNames, *(ChunkStorageFloder / TEXT("*.pak")), true, false);

	TArray<FString> VersionChunkFileNames;
	VersionChunkFileNames.Add(FString::Printf(TEXT("pakchunk0-%s.pak"), *CurrentVersionManifestInfo->Platform));
	for (auto ChunkManifestInfo : CurrentVersionManifestInfo->ChunkManifestInfos)
		VersionChunkFileNames.Add(FString::Printf(TEXT("%s-%s.pak"), *ChunkManifestInfo.Value->ChunkName, *CurrentVersionManifestInfo->Platform));

	FPakPlatformFile* PakFileMgr = (FPakPlatformFile*)(FPlatformFileManager::Get().FindPlatformFile(TEXT("PakFile")));

	// UnMount and Clear All The Un Version pak
	for (auto ChunkFileName : AllChunkFileNames)
	{
		if (!VersionChunkFileNames.Contains(ChunkFileName))
		{
			FString ChunkFileFile = FPaths::Combine(ChunkStorageFloder, ChunkFileName);
			if (PakFileMgr)
				PakFileMgr->Unmount(*ChunkFileFile);
			IFileManager::Get().Delete(*ChunkFileFile);
		}
	}
}

void UHotChunkComponent::DoTryAgain()
{
	switch (HotChunkSave->HotChunkStatus)
	{
	case EHotChunkStatus::LoadWebInfo:
	{
		FHttpRequestPtr HttpRequest = FHttpModule::Get().CreateRequest();
		HttpRequest->SetURL(FPaths::Combine(HotChunkSave->WebSites[RequireWebSiteIndex], VersionWebName));
		HttpRequest->SetVerb(TEXT("GET"));
		HttpRequest->SetHeader(TEXT("Content-Type"), TEXT("application/json"));
		HttpRequest->OnProcessRequestComplete().BindUObject(this, &UHotChunkComponent::RequireVersionWebCompleted);
		HttpRequest->ProcessRequest();
	}
	break;
	case EHotChunkStatus::LoadVersionManifest:
	{
		FHttpRequestPtr NewHttpRequest = FHttpModule::Get().CreateRequest();
		NewHttpRequest->SetURL(FPaths::Combine(HotChunkSave->WebSites[RequireWebSiteIndex], VersionWebName));
		NewHttpRequest->SetVerb(TEXT("GET"));
		NewHttpRequest->SetHeader(TEXT("Content-Type"), TEXT("application/json"));
		NewHttpRequest->OnProcessRequestComplete().BindUObject(this, &UHotChunkComponent::RequireVersionWebCompleted);
		NewHttpRequest->ProcessRequest();
	}
	break;
	case EHotChunkStatus::LoadChunkContents:
	{
		UHotChunkLibrary::RequestChunk(
			DifferChunkManifestInfos[RequireChunkContentIndex]->ChunkName,
			FPaths::Combine(VersionWebInfo->ResSites[RequireResSiteIndex], VersionWebInfo->ManifestDir, DifferChunkManifestInfos[RequireChunkContentIndex]->Filename),
			FPaths::Combine(VersionWebInfo->ResSites[RequireResSiteIndex], VersionWebInfo->CloudDir),
			ChunkStorageFloder,
			FOnRequestChunkSucceeded::CreateUObject(this, &UHotChunkComponent::RequestChunkContentSucceeded),
			FOnRequestChunkFailed::CreateUObject(this, &UHotChunkComponent::RequestChunkContentFailed)
		);
	}
	break;
	case EHotChunkStatus::InstallChunkContents:
	{
		ChunkPendingContents[InstallChunkContentIndex]->StartInstall(
			FOnChunkInstallSucceeded::CreateUObject(this, &UHotChunkComponent::InstallChunkContentSucceeded),
			FOnChunkInstallFailed::CreateUObject(this, &UHotChunkComponent::InstallChunkContentFailed)
		);
	}
	break;
	}
}


void UHotChunkComponent::RequireVersionWebCompleted(FHttpRequestPtr HttpRequest, FHttpResponsePtr HttpResponse, bool IsSucceeded)
{
	VersionWebInfo = UHotChunkFile::ConvertJsonToVersionVersionWebInfo(HttpResponse->GetContentAsString());

	if (!IsSucceeded || !VersionWebInfo.IsValid())
	{
		RequireWebSiteIndex++;
		if (RequireWebSiteIndex >= HotChunkSave->WebSites.Num())
		{
			// tell Player Connect Failed
			HotChunkStateSumbit.ExecuteIfBound("Connect Failed, Please Check Network ! [1]");

			// show ReCheck Button
			ShowReCheckAction.ExecuteIfBound();
		}
		else
		{
			SaveHotChunkStatus(EHotChunkStatus::LoadVersionManifest);

			FHttpRequestPtr NewHttpRequest = FHttpModule::Get().CreateRequest();
			NewHttpRequest->SetURL(FPaths::Combine(HotChunkSave->WebSites[RequireWebSiteIndex], VersionWebName));
			NewHttpRequest->SetVerb(TEXT("GET"));
			NewHttpRequest->SetHeader(TEXT("Content-Type"), TEXT("application/json"));
			NewHttpRequest->OnProcessRequestComplete().BindUObject(this, &UHotChunkComponent::RequireVersionWebCompleted);
			NewHttpRequest->ProcessRequest();
		}

		return;
	}

	RequireResSiteIndex = 0;

	// load Platform Manifest
	FHttpRequestPtr NewHttpRequest = FHttpModule::Get().CreateRequest();

	FString NewWebSite = FPaths::Combine(VersionWebInfo->ResSites[RequireResSiteIndex], VersionWebInfo->CloudDir, VersionWebInfo->VersionFile);
	NewHttpRequest->SetURL(NewWebSite);

	//NewHttpRequest->SetURL(FPaths::Combine(VersionWebInfo->ResSites[RequireResSiteIndex], VersionWebInfo->CloudDir, VersionWebInfo->VersionFile));
	NewHttpRequest->SetVerb(TEXT("GET"));
	NewHttpRequest->SetHeader(TEXT("Content-Type"), TEXT("application/json"));
	NewHttpRequest->OnProcessRequestComplete().BindUObject(this, &UHotChunkComponent::RequirePlatformManifestCompleted);

	NewHttpRequest->ProcessRequest();
}

void UHotChunkComponent::RequirePlatformManifestCompleted(FHttpRequestPtr HttpRequest, FHttpResponsePtr HttpResponse, bool IsSucceeded)
{
	NewVersionManifestInfo = UHotChunkFile::ConvertJsonToVersionManifestInfo(HttpResponse->GetContentAsString());

	if (NewVersionManifestInfo.IsValid() && !IsSucceeded)
	{
		RequireResSiteIndex++;

		if (RequireResSiteIndex >= VersionWebInfo->ResSites.Num())
		{
			// tell Player Connect Failed
			HotChunkStateSumbit.ExecuteIfBound("Connect Failed, Please Check Network ! [2]");

			// show ReCheck Button
			ShowReCheckAction.ExecuteIfBound();
		}
		else
		{
			FHttpRequestPtr NewHttpRequest = FHttpModule::Get().CreateRequest();
			NewHttpRequest->SetURL(FPaths::Combine(VersionWebInfo->ResSites[RequireResSiteIndex], VersionWebInfo->CloudDir, VersionWebInfo->VersionFile));
			NewHttpRequest->SetVerb(TEXT("GET"));
			NewHttpRequest->SetHeader(TEXT("Content-Type"), TEXT("application/json"));
			NewHttpRequest->OnProcessRequestComplete().BindUObject(this, &UHotChunkComponent::RequirePlatformManifestCompleted);

			NewHttpRequest->ProcessRequest();
		}
		return;
	}


	// check Differ Chunk
	if (CurrentVersionManifestInfo.IsValid())
	{
		if (NewVersionManifestInfo->ClientVersion.Equals(CurrentVersionManifestInfo->ClientVersion) && NewVersionManifestInfo->ChunkManifestInfos.Num() == CurrentVersionManifestInfo->ChunkManifestInfos.Num())
		{
			CurrentVersionSumbit.ExecuteIfBound(FString::Printf(TEXT("CurrentVersion : %s"), *NewVersionManifestInfo->ClientVersion));
			NewVersionSumbit.ExecuteIfBound(FString::Printf(TEXT("NewVersion : %s"), *NewVersionManifestInfo->ClientVersion));
			HotChunkStateSumbit.ExecuteIfBound("Update Completed !");
			HotChunkProgressSumbit.ExecuteIfBound(1.f);
			ShowOpenMenuAction.ExecuteIfBound();

			return;
		}


		if (NewVersionManifestInfo->ClientVersion.Equals(CurrentVersionManifestInfo->ClientVersion) && NewVersionManifestInfo->ChunkManifestInfos.Num() != CurrentVersionManifestInfo->ChunkManifestInfos.Num())
		{
			// find Require Chunks, no Abandon Chunks
			for (auto NewChunkManifestInfo : NewVersionManifestInfo->ChunkManifestInfos)
			{
				bool IsAddition = true;
				for (auto CurrentChunkManifestInfo : CurrentVersionManifestInfo->ChunkManifestInfos)
				{
					if (NewChunkManifestInfo.Value->ChunkName.Equals(CurrentChunkManifestInfo.Value->ChunkName))
					{
						if (!NewChunkManifestInfo.Value->Hash.Equals(CurrentChunkManifestInfo.Value->Hash) || !NewChunkManifestInfo.Value->Hash256.Equals(CurrentChunkManifestInfo.Value->Hash256))
							DifferChunkManifestInfos.Add(NewChunkManifestInfo.Value);
						IsAddition = false;
						break;
					}
				}
				if (IsAddition)
					DifferChunkManifestInfos.Add(NewChunkManifestInfo.Value);
			}
		}
		else if (!NewVersionManifestInfo->ClientVersion.Equals(CurrentVersionManifestInfo->ClientVersion))
		{
			// find DifferChunks
			for (auto NewChunkManifestInfo : NewVersionManifestInfo->ChunkManifestInfos)
			{
				bool IsAddition = true;
				for (auto CurrentChunkManifestInfo : CurrentVersionManifestInfo->ChunkManifestInfos)
				{
					if (NewChunkManifestInfo.Value->ChunkName.Equals(CurrentChunkManifestInfo.Value->ChunkName))
					{
						if (!NewChunkManifestInfo.Value->Hash.Equals(CurrentChunkManifestInfo.Value->Hash) || !NewChunkManifestInfo.Value->Hash256.Equals(CurrentChunkManifestInfo.Value->Hash256))
							DifferChunkManifestInfos.Add(NewChunkManifestInfo.Value);
						IsAddition = false;
						break;
					}
				}
				if (IsAddition)
					DifferChunkManifestInfos.Add(NewChunkManifestInfo.Value);
			}


			// find AbandonChunks
			for (auto CurrentChunkManifestInfo : CurrentVersionManifestInfo->ChunkManifestInfos)
			{
				bool IsAbandon = true;
				for (auto NewChunkManifestInfo : NewVersionManifestInfo->ChunkManifestInfos)
				{
					if (NewChunkManifestInfo.Value->ChunkName.Equals(CurrentChunkManifestInfo.Value->ChunkName))
					{
						if (!NewChunkManifestInfo.Value->Hash.Equals(CurrentChunkManifestInfo.Value->Hash) || !NewChunkManifestInfo.Value->Hash256.Equals(CurrentChunkManifestInfo.Value->Hash256))
							AbandonChunkManifestInfos.Add(CurrentChunkManifestInfo.Value);
						IsAbandon = false;
						break;
					}
				}
				if (IsAbandon)
					AbandonChunkManifestInfos.Add(CurrentChunkManifestInfo.Value);
			}
		}

		if (AbandonChunkManifestInfos.Num() > 0)
		{

			FPakPlatformFile* PakFileMgr = (FPakPlatformFile*)(FPlatformFileManager::Get().FindPlatformFile(TEXT("PakFile")));
			//unmount and remove AbandonChunks to the TempStoragePath
			for (auto AbandonChunkManifestInfo : AbandonChunkManifestInfos)
			{
				FString ChunkPakFile = FPaths::Combine(ChunkStorageFloder, FString::Printf(TEXT("%s-%s.pak"), *AbandonChunkManifestInfo->ChunkName, *CurrentVersionManifestInfo->Platform));

				if (IFileManager::Get().FileExists(*ChunkPakFile))
				{
					FString ToChunkPakFile = FPaths::Combine(TempStorageFolder, FString::Printf(TEXT("%s-%s.pak"), *AbandonChunkManifestInfo->ChunkName, *CurrentVersionManifestInfo->Platform));

					if (PakFileMgr)
						PakFileMgr->Unmount(*ChunkPakFile);
					IFileManager::Get().Move(*ToChunkPakFile, *ChunkPakFile);
				}
			}
		}

		//start Require Chunk Content
		if (DifferChunkManifestInfos.Num() > 0)
		{
			for (auto DifferChunkManifestInfo : DifferChunkManifestInfos)
				NewVersionManifestInfo->ChunkManifestInfos.Remove(DifferChunkManifestInfo->ChunkName);

			SaveHotChunkStatus(EHotChunkStatus::LoadChunkContents);

			RequireChunkContentIndex = 0;
			UHotChunkLibrary::RequestChunk(
				DifferChunkManifestInfos[RequireChunkContentIndex]->ChunkName,
				FPaths::Combine(VersionWebInfo->ResSites[RequireResSiteIndex], VersionWebInfo->ManifestDir, DifferChunkManifestInfos[RequireChunkContentIndex]->Filename),
				FPaths::Combine(VersionWebInfo->ResSites[RequireResSiteIndex], VersionWebInfo->CloudDir),
				ChunkStorageFloder,
				FOnRequestChunkSucceeded::CreateUObject(this, &UHotChunkComponent::RequestChunkContentSucceeded),
				FOnRequestChunkFailed::CreateUObject(this, &UHotChunkComponent::RequestChunkContentFailed)
			);
		}
		else
		{
			// no chunk pak to Require, delete TempStoragePath
			TArray<FString> AbandonFileNames;
			IFileManager::Get().FindFiles(AbandonFileNames, *(TempStorageFolder / TEXT("*.*")), true, false);
			for (auto AbandonFileName : AbandonFileNames)
				IFileManager::Get().Delete(*FPaths::Combine(TempStorageFolder, AbandonFileName));

			SaveHotChunkStatus(EHotChunkStatus::HotChunkCompleted);

			CurrentVersionSumbit.ExecuteIfBound(FString::Printf(TEXT("CurrentVersion : %s"), *NewVersionManifestInfo->ClientVersion));
			NewVersionSumbit.ExecuteIfBound(FString::Printf(TEXT("NewVersion : %s"), *NewVersionManifestInfo->ClientVersion));
			HotChunkStateSumbit.ExecuteIfBound("Update Completed !");
			HotChunkProgressSumbit.ExecuteIfBound(1.f);
			ShowOpenMenuAction.ExecuteIfBound();
		}
	}
	else
	{
		for (auto NewChunkManifestInfo : NewVersionManifestInfo->ChunkManifestInfos)
			DifferChunkManifestInfos.Add(NewChunkManifestInfo.Value);

		NewVersionManifestInfo->ChunkManifestInfos.Reset();

		//start Require Chunk Content
		if (DifferChunkManifestInfos.Num() > 0)
		{
			SaveHotChunkStatus(EHotChunkStatus::LoadChunkContents);
			RequireChunkContentIndex = 0;
			UHotChunkLibrary::RequestChunk(
				DifferChunkManifestInfos[RequireChunkContentIndex]->ChunkName,
				FPaths::Combine(VersionWebInfo->ResSites[RequireResSiteIndex], VersionWebInfo->ManifestDir, DifferChunkManifestInfos[RequireChunkContentIndex]->Filename),
				FPaths::Combine(VersionWebInfo->ResSites[RequireResSiteIndex], VersionWebInfo->CloudDir),
				ChunkStorageFloder,
				FOnRequestChunkSucceeded::CreateUObject(this, &UHotChunkComponent::RequestChunkContentSucceeded),
				FOnRequestChunkFailed::CreateUObject(this, &UHotChunkComponent::RequestChunkContentFailed)
			);
		}
		else
		{
			SaveHotChunkStatus(EHotChunkStatus::HotChunkCompleted);

			CurrentVersionSumbit.ExecuteIfBound(FString::Printf(TEXT("CurrentVersion : %s"), *NewVersionManifestInfo->ClientVersion));
			NewVersionSumbit.ExecuteIfBound(FString::Printf(TEXT("NewVersion : %s"), *NewVersionManifestInfo->ClientVersion));
			HotChunkStateSumbit.ExecuteIfBound("Update Completed !");
			HotChunkProgressSumbit.ExecuteIfBound(1.f);
			ShowOpenMenuAction.ExecuteIfBound();
		}
	}
}

void UHotChunkComponent::RequestChunkContentSucceeded(UChunkPendingContent* ResponseContent)
{
	ChunkPendingContents.Add(ResponseContent);

	RequireChunkContentIndex++;

	if (RequireChunkContentIndex >= DifferChunkManifestInfos.Num())
	{
		// calculate total size
		for (auto ChunkPendingContent : ChunkPendingContents)
			TotalRequireDiskSpace += ChunkPendingContent->GetRequiredDiskSpace();

		if (TotalRequireDiskSpace > UHotChunkFile::GetDiskFreeSpace())
		{
			ShowReCheckAction.ExecuteIfBound();
			HotChunkStateSumbit.ExecuteIfBound("No Enough Space, Please Clear Disk !");

			return;
		}

		SaveHotChunkStatus(EHotChunkStatus::InstallChunkContents);

		// Install Chunk Content One By One
		InstallChunkContentIndex = 0;

		ChunkPendingContents[InstallChunkContentIndex]->StartInstall(
			FOnChunkInstallSucceeded::CreateUObject(this, &UHotChunkComponent::InstallChunkContentSucceeded),
			FOnChunkInstallFailed::CreateUObject(this, &UHotChunkComponent::InstallChunkContentFailed)
		);

	}
	else
	{
		UHotChunkLibrary::RequestChunk(
			DifferChunkManifestInfos[RequireChunkContentIndex]->ChunkName,
			FPaths::Combine(VersionWebInfo->ResSites[RequireResSiteIndex], VersionWebInfo->ManifestDir, DifferChunkManifestInfos[RequireChunkContentIndex]->Filename),
			FPaths::Combine(VersionWebInfo->ResSites[RequireResSiteIndex], VersionWebInfo->CloudDir),
			ChunkStorageFloder,
			FOnRequestChunkSucceeded::CreateUObject(this, &UHotChunkComponent::RequestChunkContentSucceeded),
			FOnRequestChunkFailed::CreateUObject(this, &UHotChunkComponent::RequestChunkContentFailed)
		);
	}
}

void UHotChunkComponent::RequestChunkContentFailed(FText ErrorText, int32 ErrorCode)
{
	// try to change ResSite 
	RequireResSiteIndex++;

	if (RequireResSiteIndex >= VersionWebInfo->ResSites.Num())
	{
		// tell Player Connect Failed
		HotChunkStateSumbit.ExecuteIfBound("Connect Failed, Please Check Network ! [3]");

		// show ReCheck Button
		ShowReCheckAction.ExecuteIfBound();
	}
	else
	{
		UHotChunkLibrary::RequestChunk(
			DifferChunkManifestInfos[RequireChunkContentIndex]->ChunkName,
			FPaths::Combine(VersionWebInfo->ResSites[RequireResSiteIndex], VersionWebInfo->ManifestDir, DifferChunkManifestInfos[RequireChunkContentIndex]->Filename),
			FPaths::Combine(VersionWebInfo->ResSites[RequireResSiteIndex], VersionWebInfo->CloudDir),
			ChunkStorageFloder,
			FOnRequestChunkSucceeded::CreateUObject(this, &UHotChunkComponent::RequestChunkContentSucceeded),
			FOnRequestChunkFailed::CreateUObject(this, &UHotChunkComponent::RequestChunkContentFailed)
		);
	}
	return;
}

void UHotChunkComponent::InstallChunkContentSucceeded()
{
	RetryInstallCount = 0;

	NewVersionManifestInfo->ChunkManifestInfos.Add(DifferChunkManifestInfos[InstallChunkContentIndex]->ChunkName, DifferChunkManifestInfos[InstallChunkContentIndex]);

	InstallChunkContentIndex++;

	if (InstallChunkContentIndex >= ChunkPendingContents.Num())
	{
		InstallChunkContentIndex = -1;

		SaveHotChunkStatus(EHotChunkStatus::HotChunkCompleted);

		FPakPlatformFile* PakFileMgr = (FPakPlatformFile*)(FPlatformFileManager::Get().FindPlatformFile(TEXT("PakFile")));

		if (PakFileMgr)
		{
			//mount all content
			for (auto ChunkPendingContent : ChunkPendingContents)
			{
				FString ChunkPakFile = FPaths::Combine(ChunkStorageFloder, FString::Printf(TEXT("%s-%s.pak"), *ChunkPendingContent->ChunkName, *NewVersionManifestInfo->Platform));

				if (IFileManager::Get().FileExists(*ChunkPakFile))
					PakFileMgr->Mount(*ChunkPakFile, 0);
			}
		}

		//delete Manifest file
		TArray<FString> ManifestNames;
		IFileManager::Get().FindFiles(ManifestNames, *(ChunkStorageFloder / TEXT("*.manifest")), true, false);
		for (auto ManifestName : ManifestNames)
			IFileManager::Get().Delete(*FPaths::Combine(ChunkStorageFloder, ManifestName));


		//delete Temp file
		TArray<FString> AbandonFileNames;
		IFileManager::Get().FindFiles(AbandonFileNames, *(TempStorageFolder / TEXT("*.*")), true, false);
		for (auto AbandonFileName : AbandonFileNames)
			IFileManager::Get().Delete(*FPaths::Combine(TempStorageFolder, AbandonFileName));
	}
	else
	{
		SaveHotChunkStatus(EHotChunkStatus::InstallChunkContents);

		ChunkPendingContents[InstallChunkContentIndex]->StartInstall(
			FOnChunkInstallSucceeded::CreateUObject(this, &UHotChunkComponent::InstallChunkContentSucceeded),
			FOnChunkInstallFailed::CreateUObject(this, &UHotChunkComponent::InstallChunkContentFailed)
		);
	}
}

void UHotChunkComponent::InstallChunkContentFailed(FText ErrorText, int32 ErrorCode)
{
	//UE_LOG(LogTemp, Log, TEXT("HotChunk InstallChunkContentFailed ErrorText[%s] ErrorCode[%d]"), *ErrorText.ToString(), ErrorCode);

	// try to install again, three times
	RetryInstallCount++;
	if (RetryInstallCount < 3)
	{
		ChunkPendingContents[InstallChunkContentIndex]->StartInstall(
			FOnChunkInstallSucceeded::CreateUObject(this, &UHotChunkComponent::InstallChunkContentSucceeded),
			FOnChunkInstallFailed::CreateUObject(this, &UHotChunkComponent::InstallChunkContentFailed)
		);
	}
}

void UHotChunkComponent::SaveHotChunkStatus(EHotChunkStatus HotChunkStatus)
{
	HotChunkSave->HotChunkStatus = HotChunkStatus;
	if (NewVersionManifestInfo.IsValid())
	{
		HotChunkSave->VersionManifest = UHotChunkFile::ConvertVersionManifestInfoToJson(NewVersionManifestInfo);
		UGameplayStatics::SaveGameToSlot(HotChunkSave, HotChunkSaveName, 0);
	}
}
